import * as qiniu from 'qiniu-js'
import plupload from 'plupload'
import send from '../store/api/http'
import {cloneDeep} from 'lodash'
import {Message} from 'element-ui'
import {UPLOAD_LIMITED_TYPE} from '@/utils/m7Utils'
import {checkform} from './validate'
import {m7Language} from './m7Utils'
import store from '../store'
import Upload from './upload'
import { CONTENT_TYPE_MAP, isUseQiniu, CONTENT_TYPE_WHITE_LIST, AUTO_CHECK_LIST } from './constance'


export const getSuffix = url => {
  const cmpUrl = url.split('?')[0]
  const list = cmpUrl.split('.')
  return list[list.length - 1]
}

export const checkImgBySuffix = (url) => {
  return CONTENT_TYPE_WHITE_LIST.some(i => i === getSuffix(url))
}
/*
* 使用plupload插件上传
* @param {Object} config 上传配置(参照https://www.plupload.com/docs/v2/Uploader)
* */
export function Uploader(config) {
  var uploadUrl = null
  var token = null
  var indexCount = 0;
  var resume = false;
  var chunk_size;
  var blockSize;
  var key;
  var filesMap = {}
  var putUrlMap = {}
  var putExtra = {
    fname: "",
    params: {},
    mimeType: null
  };
  const { preDomain, useContentType = false, ctxTypeAutoCheck } = config
  var _FileUploaded_Handler = config.init && config.init.FileUploaded;
  var _Error_Handler = config.init && config.init.Error;
  config.init.FileUploaded = function() {};
  config.init.Error = function() {};
  const useQiniu = isUseQiniu()
  var keyFun = config.init.Key
  var uploader = new plupload.Uploader(useQiniu ? config : {
    ...config,
    http_method: 'PUT'
  });
  
  const isUseMultiUpload = !useQiniu && (config.multi_selection !== false)
  if (!useQiniu) {
    uploader = new Upload(config)
  }
  function getToken (uploader, files) {
    filesMap = {}
    let arr = files.map((file) => {
      let req = cloneDeep(uploader.getOption("uptoken_url"))
      let key = keyFun(uploader, file)
      key && (uploader.fileKey = key)
      filesMap[file.name] = {key}
      req.data.key = key
      return send.fetch(req, 'get', true)
    })
    return Promise.all(arr).then(([...res], i) => {
      files.forEach((file, index) => {
        filesMap[file.name].uptoken = res[index].uptoken
      })
      return useQiniu ? res[0].uptoken : res
      // return useQiniu ?  res[0].uptoken : res[0]
    })
  }
  uploader.init();

  uploader.bind("Error", function (up, err) {
    var errTip = '';
    var file = err.file;
    if (file) {
      switch (err.code) {
        case plupload.FAILED:
          errTip = '上传失败。请稍后再试。';
          break;
        case plupload.FILE_SIZE_ERROR:
          var max_file_size = up.getOption && up.getOption('max_file_size');
          max_file_size = max_file_size || (up.settings && up.settings.max_file_size);
          //errTip = '浏览器最大可上传' + max_file_size + '。更大文件请使用命令行工具。';
          errTip = '最大可上传' + max_file_size + '。';
          break;
        case plupload.FILE_EXTENSION_ERROR:
          errTip = '文件类型错误，请稍后重试';
          break;
        case plupload.HTTP_ERROR:
          if (err.response === '') {
            // Fix parseJSON error ,when http error is like net::ERR_ADDRESS_UNREACHABLE
            errTip = err.message || '未知网络错误。';
            break;
          }
          var errorObj = JSON.parse(err.response);
          var errorText = errorObj.error;
          switch (err.status) {
            case 400:
              errTip = "请求报文格式错误。";
              break;
            case 401:
              errTip = "客户端认证授权失败。请重试或提交反馈。";
              break;
            case 405:
              errTip = "客户端请求错误。请重试或提交反馈。";
              break;
            case 579:
              errTip = "资源上传成功，但回调失败。";
              break;
            case 599:
              errTip = "网络连接异常。请重试或提交反馈。";
              break;
            case 614:
              errTip = "文件已存在。";
              try {
                errorObj = JSON.parse(errorObj.error);
                errorText = errorObj.error || 'file exists';
              } catch (e) {
                errorText = errorObj.error || 'file exists';
              }
              break;
            case 631:
              errTip = "指定空间不存在。";
              break;
            case 701:
              errTip = "上传数据块校验出错。请重试或提交反馈。";
              break;
            default:
              errTip = "未知错误。";
              break;
          }
          errTip = errTip + '(' + err.status + '：' + errorText + ')';
          break;
        case plupload.SECURITY_ERROR:
          errTip = '安全配置错误。请联系网站管理员。';
          break;
        case plupload.GENERIC_ERROR:
          errTip = '上传失败。请稍后再试。';
          break;
        case plupload.IO_ERROR:
          errTip = '上传失败。请稍后再试。';
          break;
        case plupload.INIT_ERROR:
          errTip = '网站配置错误。请联系网站管理员。';
          uploader.destroy();
          break;
        default:
          errTip = err.message + err.details;
          if (!unknow_error_retry(file)) {
            return;
          }
          break;
      }
      if (_Error_Handler) {
        _Error_Handler(up, err, errTip);
      }
    } else {
      // 网络中断后回执错误
      if (_Error_Handler) {
        _Error_Handler(up, err, m7Language('webchat.uploadError'));
      }
    }
    up.refresh(); // Reposition Flash/Silverlight
  })
  uploader.bind("FilesAdded", function (uploader, files) {
    if(config.multi_number>0&&files.length>config.multi_number){
        Message.error(m7Language('webchat.simultaneousUpload',{number:config.multi_number}));
        files.forEach((file) => {
          uploader.removeFile(file)
        })
        return;
    }
    if(config.multi_number>0 && files.length + store.state.webchat.emoticonNumber>99){
      Message.error(m7Language('webchat.over99',{number:store.state.webchat.emoticonNumber}))
      files.forEach((file) => {
        uploader.removeFile(file)
      })
      return
    }
    resume = false;
    let arrType = UPLOAD_LIMITED_TYPE
    let valide = files.find((file) => {
      let arr = file.name.split('.')
      if (arrType.indexOf(arr[arr.length - 1]) !== -1) {
        return true
      } 
    })
    if (valide) {
      Message.error("该文件类型禁止上传")
      files.forEach((file) => {
        uploader.removeFile(file)
      })
      return
    }
    let valideCharacter = files.find((file) => {
      let arr = file.name.split('.')
      if (!checkform(file.name, 'specialCharacter')) {
        return true
      } 
    })
    if (valideCharacter) {
      Message.error(m7Language('public.uploadValid'))
      files.forEach((file) => {
        uploader.removeFile(file)
      })
      return
    }

    // 上传之前获取token
    getToken(uploader, files).then((resList) => {
    // getToken(uploader, files).then((res) => {
      const cmpList = typeof resList === 'string' ? [resList] : resList
      // 获取上传地址
      cmpList.forEach((res, index) => {
        let urlConfig = {}
        let m7UpHosts = uploader.getOption("m7UpHosts")
        if (m7UpHosts) {
          // m7uphosts存在则为私有云，直接取值不发请求
          urlConfig = {uphost: m7UpHosts}
        }
        if (useQiniu) {
          qiniu.getUploadUrl(urlConfig, res).then(function(res){
            uploadUrl = res
            uploader.setOption("url", uploadUrl)
            // 开始上传
            uploader.start()
          })
        } else {
          const file = files[index]
          putUrlMap[file.id] = res.putUrl
          uploadUrl = res.putUrl
          file.putUrl = res.putUrl
          if (!isUseMultiUpload) {
            uploader.start()
          }
        }
      })
      if (isUseMultiUpload) {
        uploader.start()
      }
    })
  })
  uploader.bind("BeforeUpload", function(uploader, file) {
    // 生成上传标识key值
    key = filesMap[file.name] && filesMap[file.name].key
    token = filesMap[file.name] && filesMap[file.name].uptoken
    if (preDomain && !isUseQiniu) {
      key = `${preDomain}${key}`
    }
    putExtra.params["x:name"] = file.name.split(".")[0];
    var id = file.id;
    chunk_size = uploader.getOption("chunk_size");
    let m7UpHosts = uploader.getOption("m7UpHosts")
    if (m7UpHosts && chunk_size) {
      chunk_size = '50mb'
    }
    // 直接上传
    var directUpload = function() {
      var multipart_params_obj = {};
      multipart_params_obj.token = token;
      // filterParams 返回符合自定义变量格式的数组，每个值为也为一个数组，包含变量名及变量值
      var customVarList = qiniu.filterParams(putExtra.params);
      for (var i = 0; i < customVarList.length; i++) {
        var k = customVarList[i];
        multipart_params_obj[k[0]] = k[1];
      }
      multipart_params_obj.key = key;
      // uploader.settings.url = isUseQiniu ? uploadUrl : putUrlMap[file.id],
      uploader.setOption({
        url: isUseQiniu ? uploadUrl : putUrlMap[file.id],
        multipart: true,
        multipart_params: multipart_params_obj
      });
    };
    // 分块上传
    var resumeUpload = function() {
      blockSize = chunk_size;
      initFileInfo(file);
      if(blockSize === 0){
        // 合成文件
        mkFileRequest(file)
        uploader.stop()
        return
      }
      resume = true;
      var multipart_params_obj = {};
      // 计算已上传的chunk数量
      var index = Math.floor(file.loaded / chunk_size);
      var headers = qiniu.getHeadersForChunkUpload(token)
      uploader.setOption({
        url: uploadUrl + "/mkblk/" + blockSize,
        multipart: false,
        required_features: "chunks",
        headers: {
          Authorization: "UpToken " + token
        },
        multipart_params: multipart_params_obj
      });
    };
    if (!useQiniu) {
      uploader.features.multipart = false
      uploader.features.chunks = false
    }
    // 判断是否采取分片上传
    if (
      (uploader.runtime === "html5" || uploader.runtime === "flash") &&
      chunk_size
    ) {
      if (!useQiniu) {
        return directUpload();
      } 
      if (file.size < chunk_size) {
        directUpload();
      } else {
        resumeUpload();
      }
    } else {
      console.log(
        "directUpload because file.size < chunk_size || is_android_weixin_or_qq()"
      );
      directUpload();
    }
  });

  uploader.bind("ChunkUploaded", function(up, file, info) {
    var res = JSON.parse(info.response);
    var leftSize = info.total - info.offset;
    var chunk_size = uploader.getOption && chunk_size;
    if (leftSize < chunk_size) {
      up.setOption({
        url: uploadUrl + "/mkblk/" + leftSize
      });
    }
    up.setOption({
      headers: {
        Authorization: "UpToken " + token
      }
    });
    // 更新本地存储状态
    var localFileInfo = JSON.parse(localStorage.getItem(file.name))|| [];
    localFileInfo[indexCount] = {
      ctx: res.ctx,
      time: new Date().getTime(),
      offset: info.offset,
      percent: file.percent
    };
    indexCount++;
    localStorage.setItem(file.name, JSON.stringify(localFileInfo));
  });

// 每个事件监听函数都会传入一些很有用的参数，
// 我们可以利用这些参数提供的信息来做比如更新UI，提示上传进度等操作
  uploader.bind("UploadProgress", function(uploader, file) {
    var id = file.id;
    // 更新进度条进度信息;
    var fileUploaded = file.loaded || 0;
    var count = Math.ceil(file.size / chunk_size);
    if (file.size > chunk_size) {
      updateChunkProgress(file, chunk_size, count);
    }
  });

  uploader.bind("FileUploaded", function(uploader, file, info) {
    file.fileKey = filesMap[file.name] ? filesMap[file.name].key : ''
    var id = file.id;
    if (resume) {
      mkFileRequest(file)
    } else {
      uploadFinish(JSON.parse(info.response || '{}'), file.name);
      uploader.refresh()
      _FileUploaded_Handler(uploader, file, info.response)
    }
  });
  function initFileInfo(file) {
    var localFileInfo = JSON.parse(localStorage.getItem(file.name))|| [];
    indexCount = 0;
    var length = localFileInfo.length
    if (length) {
      var clearStatus = false
      for (var i = 0; i < localFileInfo.length; i++) {
        indexCount++
        if (isExpired(localFileInfo[i].time)) {
          clearStatus = true
          localStorage.removeItem(file.name);
          break;
        }
      }
      if(clearStatus){
        indexCount = 0;
        return
      }
      file.loaded = localFileInfo[length - 1].offset;
      var leftSize = file.size - file.loaded;
      if(leftSize < chunk_size){
        blockSize = leftSize
      }
      file.percent = localFileInfo[length - 1].percent;
      return
    }else{
      indexCount = 0
    }
  }

  function mkFileRequest(file){
    // 调用sdk的url构建函数
    var requestUrl = qiniu.createMkFileUrl(
      uploadUrl,
      file.size,
      key,
      putExtra
    );
    var ctx = []
    var id = file.id
    var local = JSON.parse(localStorage.getItem(file.name))
    for(var i =0;i<local.length;i++){
      ctx.push(local[i].ctx)
    }
    // 设置上传的header信息
    var headers = qiniu.getHeadersForMkFile(token)
    return window.fetch(requestUrl, {
      method: useQiniu ? 'post' : 'put',
      body: ctx.join(","),
      headers: headers
    }).then((res) => {
      let response = res.json()
      response.then((res) => {
        uploadFinish(res, file.name);
        _FileUploaded_Handler(uploader, file, JSON.stringify(res))
      })
    })
  }
  function uploadFinish(res, name) {
    localStorage.removeItem(name)
  }
  return uploader
}

function updateChunkProgress(file, chunk_size, count) {

  var index = Math.ceil(file.loaded / chunk_size);
  var leftSize = file.loaded - chunk_size * (index - 1);
  if (index == count) {
    chunk_size = file.size - chunk_size * (index - 1);
  }
}
function isExpired(time){
  let expireAt = time + 3600 * 24* 1000;
  return new Date().getTime() > expireAt;
}
/*
* 七牛直接上传
* @param {Object} config 上传配置
* @param {Object} config.uptoken_url 获取token请求对象
* @param {String} config.m7UpHosts 私有云上传地址，公有云传false
* @param {Object} config.file 上传文件blob
* @param {String} config.fileName 上传文件名称
* @param {String} config.key 上传标识(下载时使用)
* @param {Function} config.next 上传过程函数
* @param {Function} config.error 上传错误函数
* @param {Function} config.complete 上传完成函数
* */
export function qiniuUpload(config) {
  let req = config.uptoken_url
  // 上传之前获取token
  let arrType = UPLOAD_LIMITED_TYPE
  let arr = config.fileName.split('.')
  if (arrType.indexOf(arr[arr.length - 1]) !== -1) {
    Message.error("该文件类型禁止上传")
    return
  }
  if (!checkform(config.fileName, 'specialCharacter')) {
    Message.error(m7Language('public.uploadValid'))
    return
  }
  const useQiniu = isUseQiniu()
  req.data.key = config.key
  send.fetch(req, 'get', true).then((res) => {
    let token = res.uptoken
    // 获取上传地址
    let urlConfig = {}
    let m7UpHosts = config.m7UpHosts
    if (m7UpHosts) {
      // m7uphosts存在则为私有云，直接取值不发请求
      urlConfig = {uphost: m7UpHosts}
    }
    if (!useQiniu) {
      const { domain } = config
      return new FileUpload({
        unUseUploadOnload: config.unUseUploadOnload,
        useContentType: config.useContentType,
        target: res.putUrl,
        onProgress: config.next,
        onError: config.error,
        onComplete: config.complete,
        file: config.file,
        beforeUpload: config.beforeUpload,
        query: {
          key: domain ? `${domain}${config.key}` : config.key,
          'x:name': config.file.name,
          name: config.file.name
        }
      })
    }
    let putExtra = {
      fname: config.fileName,
      params: {},
      mimeType: null
    };
    let observable = qiniu.upload(config.file, config.key, token, putExtra, urlConfig)
    let subObject = {
      next: config.next,
      error: config.error,
      complete: config.complete
    }
    observable.subscribe(subObject)
  })
}


class FileUpload {
  constructor (params) {
    this.params = params
    this.init()
  }

  buildUrl(url, items) {
    let res = url
		let query = '';

		plupload.each(items, function(value, name) {
			query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
		});

		if (query) {
			res += (res.indexOf('?') > 0 ? '&' : '?') + query;
		}

		return res;
	}

  async init() {
    const { method = 'PUT', file, onProgress, onError, onComplete, target, query = {}, beforeUpload, unUseUploadOnload } = this.params
    if (typeof beforeUpload === 'function') {
      const res = await beforeUpload(file)
      if (!res) {
        return
      }
    }
    const xhr = new XMLHttpRequest()
    xhr.open(method, this.buildUrl(target, query), true)
    const suffix = getSuffix(file.name) 
    const contentType = CONTENT_TYPE_MAP[suffix] || 'application/octet-stream'
    // xhr.setRequestHeader('content-type', useContentType ?`${contentType};charset=UTF-8` : 'application/x-www-form-urlencoded;charset=UTF-8')
    xhr.setRequestHeader('content-type', `${contentType};charset=UTF-8`)
    xhr.upload.onprogress = function(e) {
      // e.lengthComputable 是一个布尔值，表示当前上传的资源是否具有可计算的长度
      if (e.lengthComputable) {
          // e.loaded 已传输的字节
          // e.total 需传输的总字节
          const percent = Math.ceil((e.loaded / e.total) * 100)
          // onProgress(percentComplete)
          typeof onProgress === 'function' && onProgress({
            total: {
              percent
            },
            file
          })
      }
    }
    let hasComplete = false
    xhr.upload.onload = e => {
      if (unUseUploadOnload) {
        return
      }
      onComplete({
        fileKey: query.key,
        file
      })
      hasComplete = true
    }
    xhr.upload.onerror = () => {
      Message.error(m7Language('webchat.uploadError'))
    }
    xhr.send(file)
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
          if (xhr.status === 200) { // 上传文件成功
            if (!hasComplete) {
              onComplete({
                fileKey: query.key,
                file
              })
            }
          } else { // 上传文件失败
            Message.error(m7Language('webchat.uploadError'))
            onError()
          }
      }
     }
  }
}
